K8s 应用发布方案 - 金丝雀

K8s 应用发布方案

项目背景

假设目标场景为需要发布名称为 DataAPI 的应用,此应用在 k8s 集群上共有 5 个 Pod,均为无状态的应用,分布在 2 个节点上。假设目前生产使用版本为 v1,待发布版本为 v2。

准备镜像

已推送镜像 ali-hb-test.huifutest.com/app/dataapi-canary 的 v1, v2 版本到指定测试镜像仓库。

基于 Deployment 创建 Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: apps/v1
kind: Deployment
metadata:
name: dataapi-canary-deploy
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: dataapi
release: canary
template:
metadata:
labels:
app: dataapi
release: canary
spec:
containers:
- name: dataapi-canary
image: ali-art.huifutest.com/dev-v/app/dataapi-canary:v1
ports:
- name: http
containerPort: 80
1
k apply -f dataapi-canary.yaml
1
2
3
4
5
6
7
8
9
10
11
k get pods
NAME READY STATUS RESTARTS AGE
dataapi-canary-deploy-566b698f7f-csm8l 1/1 Running 0 58s
dataapi-canary-deploy-566b698f7f-kcfbf 1/1 Running 0 48s
dataapi-canary-deploy-566b698f7f-mkvv5 1/1 Running 0 50s
dataapi-canary-deploy-566b698f7f-qrbsf 1/1 Running 0 1m
dataapi-canary-deploy-566b698f7f-x8dgh 1/1 Running 0 57s
dataapi-deployment-7f9bf79875-cftpx 1/1 Running 0 5d
hmas-server-deployment-74bffc5f8c-q9dcm 1/1 Running 0 8d
mind3-base-658944fcff-d544g 1/1 Running 14 4d
vango-deployment-56484d676d-xrr9p 1/1 Running 0 4d

创建 Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: dataapi-canary-service
namespace: default
spec:
selector:
app: dataapi
release: canary
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30800
1
k apply -f dataapi-canary-service.yaml
1
2
3
 k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dataapi-canary-service NodePort 172.16.1.57 <none> 80:30800/TCP 2m

访问测试

1
2
curl http://192.168.25.38:30800
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

可以看到访问返回的内容为 V1

1
while true ;do sleep 2; curl http://192.168.25.38:30800/hostname.html; done;

每隔两秒请求一次接口,可以看到 svc 是带有负载均衡效果的,平均分配到不同的 pod 上

金丝雀发布

核心思想

为什么叫金丝雀发布(Canary)?以前,旷工开矿,在下矿洞前需要检查下方是否有毒气,矿工们先会放一只金丝雀进去探是否有有毒气体,看金丝雀能否活下来。
金丝雀发布平是一种平滑过渡的一种发布方式 。产品上线,一方面要保证质量,另一方面保证刚上线的系统没有问题,这时候就需要设计一套灰度发布系统,让一部分用户继续使用产品A,另一部用户使用产品B,一旦出现问题可以很快的控制影响,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户流量都迁移到B上。

基于 replicas 实现

新建一个 deploy 配置文件,命名为 dataapi-canary-replicas.yaml。内容主要修改镜像版本为 v2,修改 replicas 为1,修改 deploy 名称为 dataapi-canary-deploy-v2,且 Pod 新增一个标签 version: v2.0 。详细信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
name: dataapi-canary-deploy-v2
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: dataapi
release: canary
template:
metadata:
labels:
app: dataapi
release: canary
version: v2.0
spec:
containers:
- name: dataapi-canary
image: ali-art.huifutest.com/dev-v/app/dataapi-canary:v2
ports:
- name: http
containerPort: 80

新建 Pod 之后会看见一个新创建的 Pod

新建出来的 Pod 因为 包含标签 app: dataapi,release: canary 所以也能被 Service 标签选择器选中,并且我们设置数量为1(相当于放出一只金丝雀),我们进行访问测试,可以看到能访问到 v2 的接口。这里我们还能动态的设置新建的 deploy 的 replicas 数量,来实现测试不同的访问流量占比。

在真正的生产环境中我们现在就应该观察新发布的 v2 版本接口是否正常,如果正常那我们可以将 replicas 数量设置为之前 DataAPI 期待的 5个,并将之前的 deploy 删除,以此来实现平滑的过度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
name: dataapi-canary-deploy-v2
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: dataapi
release: canary
template:
metadata:
labels:
app: dataapi
release: canary
version: v2
spec:
containers:
- name: dataapi-canary
image: ali-art.huifutest.com/dev-v/app/dataapi-canary:v2
ports:
- name: http
containerPort: 80

删除之前的 deploy

进行访问测试 就可以发现全部访问結果都为 v2 了,以此来实现平滑过渡,达到金丝雀发布的目的。

基于 strategy 实现

【注意】测试 rollout pause 之前,已将 Pod 镜像回滚至 v1 状态。
在 deployment 控制器中 spec 下有一个 strategy 字段能让我们灵活的配置 Pod 更新策略,查看描述信息如下:

type 为部署类型。可以是 “重新创建” 或 “滚动更新”。默认为滚动更新。当 type 为 “RollingUpdate” 时,我们可以配置 RollingUpdate 字段。RollingUpdate 字段详细信息如下:

其中 maxSurge 代表更新过程中新建 Pod 最大数量或者比例, maxUnavailable 为更新过程中删除 Pod 的数量或者比例。我们可以动态的调节这两个参数来实现 “金丝雀发布” 或者 “蓝绿部署”。
举例:目前 DataAPI Pod 部署数量为 5,假如我们设置 maxSurge 为 1,maxUnavailable 为 0,即表示在更新过程中只能最大新增一个 Pod,而能删减的 Pod 数量为 0,这时 DataAPI 存活的 Pod 数量为 6 个。由于我们设置的 replicas 为 5,也就是说我们期待的理想状态 Pod 数量是为 5个,但是现在为 6 个,肯定不符合需求!所以 deploy 控制器就会删除一个原来的 Pod。那有什么方法是在不影响以前的 Pod 上新增这一个 “金丝雀” 呢?这时我们就可以使用 pause 了。
​ pause 的作用是将发布进行暂停,此时将不受控制器控制。描述信息如下:

基于以上核心思想,我们就可以发布 v2 版本的 DataAPI 程序了。并且通过配置 strategy 信息和 pause 实现金丝雀发布,deploy 配置文件(这里主要新增 strategy 字段信息)如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: apps/v1
kind: Deployment
metadata:
name: dataapi-canary-deploy
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: dataapi
release: canary
template:
metadata:
labels:
app: dataapi
release: canary
spec:
containers:
- name: dataapi-canary
image: ali-art.huifutest.com/dev-v/app/dataapi-canary:v1
ports:
- name: http
containerPort: 80
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0

执行 apply 更新 strategy信息:

1
k apply -f dataapi-canary.yaml

查看 deploy 详细信息

1
k describe deploy dataapi-canary-deploy

修改 deploy 文件,将 DataAPI 发布镜像版本改为 v2。并更新 Pod,让更新暂停

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: apps/v1
kind: Deployment
metadata:
name: dataapi-canary-deploy
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: dataapi
release: canary
template:
metadata:
labels:
app: dataapi
release: canary
spec:
containers:
- name: dataapi-canary
image: ali-art.huifutest.com/dev-v/app/dataapi-canary:v2
ports:
- name: http
containerPort: 80
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
1
kubectl apply -f dataapi-canary.yaml && kubectl rollout pause deploy  dataapi-canary-deploy

查看 Pod 可以看到新建了一个 Pod(相当于放出的金丝雀)

我们进行访问测试

1
while true ; do curl http://192.168.25.38:30800; done;

可以看到访问的测试中,已经有了 v2 版本的请求响应。这时在真正的生产发布中我们就可以对 v2 版本的接口进行测试了,如果没有问题就可以继续发布下去了。将其余 v1 版本的接口完全替换为 v2 版本的接口。

1
kubectl rollout resume deploy dataapi-canary-deploy

再次进行测试,就会发现服务已经更新为了 v2 版本。也就完成了一个完整的金丝雀发布。

总结

第一种方式较为简单,第二种方式比较难懂且较为复杂。合理的利用标签选择器和 strategy 还能实现蓝绿部署,A/B 测试等策略。